if (typeof jQuery == 'undefined') {
	alert("need jquery");
} if(typeof Strophe == 'undefined'){
	alert("need Strophe");
} else {

(function($) {
	
if (typeof Easemob == 'undefined') {
	Easemob = {};
}
if (typeof Easemob.im == 'undefined') {
	Easemob.im = {};
}
if (typeof Easemob.im.Connection !== 'undefined') {
	return;
}

var innerBase64 = (function() {
	var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

	var obj = {
		/**
		 * Encodes a string in base64
		 * 
		 * @param {String}
		 *            input The string to encode in base64.
		 */
		encode : function(input) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			do {
				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}

				output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2)
						+ keyStr.charAt(enc3) + keyStr.charAt(enc4);
			} while (i < input.length);

			return output;
		},

		byteEncode : function(bytes) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			do {
				chr1 = bytes[i++];
				chr2 = bytes[i++];
				chr3 = bytes[i++];

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}

				output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2)
						+ keyStr.charAt(enc3) + keyStr.charAt(enc4);
			} while (i < bytes.length);

			return output;
		},

		/**
		 * Decodes a base64 string.
		 * 
		 * @param {String}
		 *            input The string to decode.
		 */
		decode : function(input) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

			do {
				enc1 = keyStr.indexOf(input.charAt(i++));
				enc2 = keyStr.indexOf(input.charAt(i++));
				enc3 = keyStr.indexOf(input.charAt(i++));
				enc4 = keyStr.indexOf(input.charAt(i++));

				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;

				output = output + String.fromCharCode(chr1);

				if (enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}
			} while (i < input.length);

			return output;
		}
	};

	return obj;
})();

var emptyFn = function() {};

var tempIndex = 0;
EASEMOB_IM_CONNCTION_USER_NOT_ASSIGN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_OPEN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_AUTH_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_ATTACH_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_ATTACH_USERGRID_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_REOPEN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_SERVER_CLOSE_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_SERVER_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_IQ_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_PING_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROSTER_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_CROSSDOMAIN_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_LISTENING_OUTOF_MAXRETRIES = tempIndex++;
EASEMOB_IM_CONNCTION_RECEIVEMSG_CONTENTERROR = tempIndex++;
EASEMOB_IM_CONNCTION_JOINROOM_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOM_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOMINFO_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOMMEMBER_ERROR = tempIndex++;
EASEMOB_IM_CONNCTION_GETROOMOCCUPANTS_ERROR = tempIndex++;

EASEMOB_IM_UPLOADFILE_BROWSER_ERROR = tempIndex++;
EASEMOB_IM_UPLOADFILE_ERROR = tempIndex++;
EASEMOB_IM_UPLOADFILE_NO_LOGIN = tempIndex++;
EASEMOB_IM_UPLOADFILE_NO_FILE = tempIndex++;
EASEMOB_IM_DOWNLOADFILE_ERROR = tempIndex++;
EASEMOB_IM_DOWNLOADFILE_NO_LOGIN = tempIndex++;
EASEMOB_IM_DOWNLOADFILE_BROWSER_ERROR = tempIndex++;

EASEMOB_IM_RESISTERUSER_ERROR = tempIndex++;

tempIndex = 0;
EASEMOB_IM_MESSAGE_REC_TEXT = tempIndex++;
EASEMOB_IM_MESSAGE_REC_EMOTION = tempIndex++;
EASEMOB_IM_MESSAGE_REC_PHOTO = tempIndex++;
EASEMOB_IM_MESSAGE_REC_AUDIO = tempIndex++;
EASEMOB_IM_MESSAGE_REC_AUDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_REC_VEDIO = tempIndex++;
EASEMOB_IM_MESSAGE_REC_VEDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_REC_FILE = tempIndex++;

EASEMOB_IM_MESSAGE_SED_TEXT = tempIndex++;
EASEMOB_IM_MESSAGE_SED_EMOTION = tempIndex++;
EASEMOB_IM_MESSAGE_SED_PHOTO = tempIndex++;
EASEMOB_IM_MESSAGE_SED_AUDIO = tempIndex++;
EASEMOB_IM_MESSAGE_SED_AUDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_SED_VEDIO = tempIndex++;
EASEMOB_IM_MESSAGE_SED_VEDIO_FILE = tempIndex++;
EASEMOB_IM_MESSAGE_SED_FILE = tempIndex++;

var emotionPicData = {
		"[):]":"",
		"[:D]":"",
		"[;)]":"",
		"[:-o]":"",
		"[:p]":"",
		"[(H)]":"",
		"[:@]":"",
		"[:s]":"",
		"[:$]":"",
		"[:(]":"",
		"[:'(]":"",
		"[:|]":"",
		"[(a)]":"",
		"[8o|]":"",
		"[8-|]":"",
		"[+o(]":"",
		"[<o)]":"",
		"[|-)]":"",
		"[*-)]":"",
		"[:-#]":"",
		"[:-*]":"",
		"[^o)]":"",
		"[8-)]":"",
		"[(|)]":"",
		"[(u)]":"",
		"[(S)]":"",
		"[(*)]":"",
		"[(#)]":"",
		"[(R)]":"",
		"[({)]":"",
		"[(})]":"",
		"[(k)]":"",
		"[(F)]":"",
		"[(W)]":"",
		"[(D)]":""
};
var createStandardXHR = function () {
	try {
		return new window.XMLHttpRequest();
	} catch( e ) {
		return false;
	}
};
var createActiveXHR = function () {
	try {
		return new window.ActiveXObject( "Microsoft.XMLHTTP" );
	} catch( e ) {
		return false;
	}
};
if (window.XDomainRequest) {
	XDomainRequest.prototype.oldsend = XDomainRequest.prototype.send;
	XDomainRequest.prototype.send = function() {
		XDomainRequest.prototype.oldsend.apply(this, arguments);
		this.readyState = 2;
	};
}

var xmlrequest = function (crossDomain){
	crossDomain = crossDomain || true;
	var temp = createStandardXHR () || createActiveXHR();
	
	if ("withCredentials" in temp) {
		return temp;
	}
	if(!crossDomain){
		return temp;
	}
	if(window.XDomainRequest===undefined){
		return temp;
	}
	var xhr = new XDomainRequest();
	xhr.readyState = 0;
	xhr.status = 100;
	xhr.onreadystatechange = emptyFn;
	xhr.onload = function () {
		xhr.readyState = 4;
		xhr.status = 200;
		
		var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.async = "false";
		xmlDoc.loadXML(xhr.responseText);
		xhr.responseXML = xmlDoc;
		xhr.response = xhr.responseText;
		xhr.onreadystatechange();
	};
	xhr.ontimeout = xhr.onerror = function(){
		xhr.readyState = 4;
		xhr.status = 500;
		xhr.onreadystatechange();
	};
	return xhr;
};
Strophe.Request.prototype._newXHR = function(){
	var xhr =  xmlrequest(true);
  if (xhr.overrideMimeType) {
      xhr.overrideMimeType("text/xml");
  }
  xhr.onreadystatechange = this.func.bind(null, this);
  return xhr;
};

function getIEVersion(){
    var ua = navigator.userAgent,matches,tridentMap={'4':8,'5':9,'6':10,'7':11};
    matches = ua.match(/MSIE (\d+)/i);
    if(matches&&matches[1])
    {   
        return +matches[1];
    }
    matches = ua.match(/Trident\/(\d+)/i);
    if(matches&&matches[1])
    {   
        return tridentMap[matches[1]]||null;
    }
    return null;
};
var ieVersion = getIEVersion();

var tepmxhr = xmlrequest();
var hasSetRequestHeader = (tepmxhr.setRequestHeader || false );
var hasOverrideMimeType = (tepmxhr.overrideMimeType || false);
tepmxhr = null;

var doAjaxRequest = function(options) {
	var dataType = options.dataType || 'text';
	var suc = options.success || emptyFn;
	var error = options.error || emptyFn;
	var xhr = xmlrequest();
	xhr.onreadystatechange = function (){
		if( xhr.readyState === 4){
			var status = xhr.status || 0;
			if (status == 200) {
				if(dataType=='text'){
					suc(xhr.responseText,xhr);
					return;
				}
				if(dataType=='json'){
					try{
						var json = $.parseJSON(xhr.responseText);
						suc(json,xhr);
					} catch(e){
						error(xhr.responseText,xhr,"错误的数据,无法转换为json");
					}
					return;
				}
				if(dataType=='xml'){
					if (xhr.responseXML && xhr.responseXML.documentElement) {
						suc(xhr.responseXML.documentElement,xhr);
					} else {
						error(xhr.responseText,xhr,"浏览器不支持ajax返回xml对象");
					}
					return;
				}
				suc(xhr.response || xhr.responseText,xhr);
				return;
			} else {
				if(dataType=='json'){
					try{
						var json = $.parseJSON(xhr.responseText);
						error(json,xhr,"服务器返回错误信息");
					} catch(e){
						error(xhr.responseText,xhr,"服务器返回错误信息");
					}
					return;
				}
				if(dataType=='xml'){
					if (xhr.responseXML && xhr.responseXML.documentElement) {
						error(xhr.responseXML.documentElement,xhr,"服务器返回错误信息");
					} else {
						error(xhr.responseText,xhr,"服务器返回错误信息");
					}
					return;
				}
				error(xhr.responseText,xhr,"服务器返回错误信息");
				return;
			}
		}
		if( xhr.readyState === 0){
			error(xhr.responseText,xhr,"服务器异常");
		}
	};

	if(options.responseType){
		if(xhr.responseType){
			xhr.responseType = options.responseType;
		} else {
			error('',xhr,"当前浏览器不支持设置响应类型");
			return null;
		}
	}
	if(options.mimeType){
		if(hasOverrideMimeType){
			xhr.overrideMimeType(options.mimeType);
		} else {
			error('',xhr,"当前浏览器不支持设置mimeType");
			return null;
		}
	}
	
	var type = options.type || "POST";
	xhr.open(type, options.url);
	
	var headers = options.headers || {};
	for(var key in headers){
		if(hasSetRequestHeader){
			xhr.setRequestHeader(key, headers[key]);
		} else {
			error('',xhr,"当前浏览器不支持设置header");
			return null;
		}
	}

	var data = options.data || null;
	xhr.send(data);
	return xhr;
};

var registerUserFn = function(options){
	var orgName = options.orgName || '';
	var appName = options.appName || '';
	var appKey = options.appKey || '';
	if(!orgName && !appName && appKey){
		var devInfos = appKey.split('#');
		if(devInfos.length==2){
			orgName = devInfos[0];
			appName = devInfos[1];
		}
	}
	if(!orgName && !appName){
		options.error({
			type : EASEMOB_IM_RESISTERUSER_ERROR,
			msg : '没有指定开发者信息'
		});
		return;
	}
	var prefix = options.https ? 'https' : 'http';
	var restUrl = options.url || prefix + '://a1.easemob.com/'+ orgName + '/' + appName + '/users';
	
	var userjson = {
			username : options.username,
			password : options.password,
			nickname : options.nickname || ''
	};
	
	var userinfo = JSON.stringify(userjson);
	var options = {
		url : restUrl,
		dataType : 'json',
		data : userinfo,
		success : options.success || emptyFn,
		error : options.error || emptyFn
	};
	var param = doAjaxRequest(options);
	return param;
};
  

var getFileUrlFn = function(fileInputId) {
	var uri = {
		url : '',
		filename : '',
		filetype : ''
	};
	if (window.URL  && window.URL.createObjectURL) {
		var fileItems = document.getElementById(fileInputId).files;
		if (fileItems.length > 0) {
			var u = fileItems.item(0);
			uri.url = window.URL.createObjectURL(u);
			uri.filename = u.name || '';
		}
	} else { // IE
		var u = document.getElementById(fileInputId).value;
		uri.url = u;
		var pos1 = u.lastIndexOf('/');
		var pos2 = u.lastIndexOf('\\');
		var pos = Math.max(pos1, pos2)
		if (pos < 0)
			uri.filename = u;
		else
			uri.filename = u.substring(pos + 1);
	}
	var index = uri.filename.lastIndexOf(".");
	if (index != -1) {
		uri.filetype = uri.filename.substring(index+1).toLowerCase();
	}
	return uri;
};
var isIe = false;
if (!!window.ActiveXObject || "ActiveXObject" in window) {
	isIe = true;
}
var getFileSizeFn = function(fileInputId){
	var file = document.getElementById(fileInputId)
	var fileSize = 0;
	if(file){
		if(file.files){
			if(file.files.length>0){
				fileSize = file.files[0].size;
			}
		} else if(isIe){
			file.select();
			var fileobject = new ActiveXObject ("Scripting.FileSystemObject");  
			var file = fileobject.GetFile (file.value);  
			fileSize = file.Size;
		}
	}
	return fileSize;
};

var hasFormData = (typeof FormData != 'undefined');
var isCanUploadFile = (hasSetRequestHeader && hasFormData);
var uploadFn = function(options) {
	options = options || {};
	options.onFileUploadProgress = options.onFileUploadProgress || emptyFn;
	options.onFileUploadComplete = options.onFileUploadComplete || emptyFn;
	options.onFileUploadError = options.onFileUploadError || emptyFn;
	options.onFileUploadCanceled = options.onFileUploadCanceled || emptyFn;

	if (!isCanUploadFile) {
		options.onFileUploadError({
			type : EASEMOB_IM_UPLOADFILE_BROWSER_ERROR,
			msg : '当前浏览器不支持异步上传文件,请换用其他浏览器'
		});
		return;
	}
	
	var acc = options.accessToken;
	if (!acc) {
		options.onFileUploadError({
			type : EASEMOB_IM_UPLOADFILE_NO_LOGIN,
			msg : '用户未登录到usergrid服务器,无法使用文件上传功能'
		});
		return;
	}

	orgName = options.orgName || '';
	appName = options.appName || '';
	appKey = options.appKey || '';
	if(!orgName && !appName && appKey){
		var devInfos = appKey.split('#');
		if(devInfos.length==2){
			orgName = devInfos[0];
			appName = devInfos[1];
		}
	}
	if(!orgName && !appName){
		options.onFileUploadError({
			type : EASEMOB_IM_UPLOADFILE_ERROR,
			msg : '没有指定开发者信息'
		});
		return;
	}
	var fileSize = getFileSizeFn(options.fileInputId);
	if(fileSize > 10485760){
		options.onFileUploadError({
			type : EASEMOB_IM_UPLOADFILE_ERROR,
			msg : '上传文件超过服务器大小限制（10M）'
		});
		return ;
	}else if(fileSize <= 0){
		options.onFileUploadError({
			type : EASEMOB_IM_UPLOADFILE_ERROR,
			msg : '上传文件大小为0'
		});
		return ;
	}
	var apiUrl = options.apiUrl || 'http://a1.easemob.com';
	var uploadUrl = apiUrl + "/" + orgName + '/' + appName + '/chatfiles';

	var xhr = xmlrequest();
	var onError = function(e) {
		options.onFileUploadError({
			type : EASEMOB_IM_UPLOADFILE_ERROR,
			msg : '上传文件失败',
			e : e,
			xhr : xhr
		});
	}
	if(xhr.upload){
		xhr.upload.addEventListener("progress",options.onFileUploadProgress, false);
	}
	if(xhr.addEventListener){
		xhr.addEventListener("abort", options.onFileUploadCanceled, false);
		xhr.addEventListener("load", function(e) {
			try{
				var json = $.parseJSON(xhr.responseText);
				options.onFileUploadComplete(json);
			} catch(e){
				options.onFileUploadError({
					type : EASEMOB_IM_UPLOADFILE_ERROR,
					msg : '上传文件失败,服务端返回值值不正确',
					e : e,
					data : xhr.responseText,
					xhr : xhr
				});
			}
		}, false);
		xhr.addEventListener("error", onError, false);
	} else if(xhr.onreadystatechange){
		xhr.onreadystatechange = function (){
			if( xhr.readyState === 4){
				if (ajax.status == 200) {
					try{
						var json = $.parseJSON(xhr.responseText);
						options.onFileUploadComplete(json);
					} catch(e){
						options.onFileUploadError({
							type : EASEMOB_IM_UPLOADFILE_ERROR,
							msg : '上传文件失败,服务端返回值不正确',
							e : e,
							data : xhr.responseText,
							xhr : xhr
						});
					}
				} else {
						options.onFileUploadError({
							type : EASEMOB_IM_UPLOADFILE_ERROR,
							msg : '上传文件失败,服务端返回异常',
							data : xhr.responseText,
							xhr : xhr
						});
				}
			} else {
				xhr.abort();
				options.onFileUploadCanceled();
			}
		}
	}

	xhr.open("POST", uploadUrl);

	xhr.setRequestHeader('restrict-access', 'true');
	xhr.setRequestHeader('Authorization', 'Bearer ' + acc);

	var localFile = '';
	var fileInput = document.getElementById(options.fileInputId);
	var localFile = null;
	if ("files" in fileInput) {
		localFile = fileInput.files[0];
	} else {
		localFile = fileInput.value;
	}
	var formData = new FormData();
	formData.append("file", localFile);
	xhr.send(formData);
};
var hasBlob = (typeof Blob != 'undefined');
var isCanDownLoadFile = (hasSetRequestHeader && (hasBlob || hasOverrideMimeType));
var downloadFn = function(options){
	options.onFileDownloadComplete = options.onFileDownloadComplete || emptyFn;
	options.onFileDownloadError = options.onFileDownloadError || emptyFn;
	
	if (!isCanDownLoadFile) {
		options.onFileDownloadError({
			type : EASEMOB_IM_DOWNLOADFILE_BROWSER_ERROR,
			msg : '当前浏览器不支持异步下载文件,请换用其他浏览器'
		});
		return;
	}
	var accessToken = options.accessToken || '';
	if (!accessToken) {
		options.onFileDownloadError({
			type : EASEMOB_IM_DOWNLOADFILE_NO_LOGIN,
			msg : '用户未登录到usergrid服务器,无法使用文件下载功能'
		});
		return;
	}
	
	var onError = function(e) {
		options.onFileDownloadError({
			type : EASEMOB_IM_DOWNLOADFILE_ERROR,
			msg : '下载文件失败',
			xhr : xhr,
			e : e
		});
	}
	var xhr = xmlrequest();
	if("addEventListener" in xhr){
		xhr.addEventListener("load", function(e) {
			options.onFileDownloadComplete(xhr.response,xhr);
		}, false);
		xhr.addEventListener("error", onError, false);
	} else if("onreadystatechange" in xhr){
		xhr.onreadystatechange = function (){
			if( xhr.readyState === 4){
				if (ajax.status == 200) {
					options.onFileDownloadComplete(xhr.response,xhr);
				} else {
						options.onFileDownloadError({
							type : EASEMOB_IM_DOWNLOADFILE_ERROR,
							msg : '下载文件失败,服务端返回异常',
							xhr : xhr
						});
				}
			} else {
				xhr.abort();
				options.onFileDownloadError({
					type : EASEMOB_IM_DOWNLOADFILE_ERROR,
					msg : '错误的下载状态,退出下载',
					xhr : xhr
				});
			}
		}
	}
	
	var method = options.method || 'GET';
	var resType = options.responseType || 'blob';
	var mimeType = options.mimeType || "text/plain; charset=x-user-defined";
	xhr.open(method, options.url);
	if(typeof Blob != 'undefined'){
		xhr.responseType = resType;
	} else {
		xhr.overrideMimeType(mimeType);
	}

	var innerHeaer = {
		'X-Requested-With' : 'XMLHttpRequest',
		'Accept' : 'application/octet-stream',
		'share-secret' : options.secret,
		'Authorization' : 'Bearer ' + accessToken
	};
	var headers = options.headers || {};
	for(var key in headers){
		innerHeaer[key] = headers[key];
	}
	for(var key in innerHeaer){
		if(innerHeaer[key]){
			xhr.setRequestHeader(key, innerHeaer[key]);
		}
	}
	xhr.send(null);
};

var parseNameFromJidFn = function(jid,domain){
	domain = domain || "";
	var tempstr = jid;
	var findex = tempstr.indexOf("_");
	if(findex!=-1){
		tempstr = tempstr.substring(findex+1);
	}
	var atindex = tempstr.indexOf("@" + domain);
	if(atindex!=-1){
		tempstr = tempstr.substring(0,atindex);
	}
	return tempstr;
};

var parseTextMessageFn = function(message){
	var receiveMsg = message;
	var emessage = [];
	var expr = /\[[^[\]]{2,3}\]/mg;
	var emotions = receiveMsg.match(expr);
	if (!emotions || emotions.length < 1){
		return {"isemotion":false,"body":[{"type" : "txt","data":message}]};
	}
	var isemotion = false;
	for (var i = 0; i < emotions.length; i++) {
		var tmsg = receiveMsg.substring(0,receiveMsg.indexOf(emotions[i]));
		if (tmsg) {
			emessage.push({
				"type" : "txt",
				"data" : tmsg
			});
		}
		var emotion = emotionPicData[emotions[i]];
		if (emotion) {
			isemotion = true;
			emessage.push({
				"type" : "emotion",
				"data" : emotion
			});
		} else {
			emessage.push({
				"type" : "txt",
				"data" : emotions[i]
			});
		}
		var restMsgIndex = receiveMsg.indexOf(emotions[i]) + emotions[i].length;
		receiveMsg = receiveMsg.substring(restMsgIndex);
	}
	if (receiveMsg) {
		emessage.push({
			"type" : "txt",
			"data" : receiveMsg
		});
	}
	if(isemotion){
		return {"isemotion":isemotion,"body":emessage};
	}
	return {"isemotion":false,"body":[{"type" : "txt","data":message}]};
}

var parseResponseMessageFn = function(msginfo){
	var parseMsgData = {errorMsg:true,data:[]};
	
	var msgBodies = msginfo.getElementsByTagName("body");
	if(msgBodies){
		for (var i=0;i<msgBodies.length;i++){
			var msgBody = msgBodies[i];
			var childNodes = msgBody.childNodes;
			if(childNodes && childNodes.length>0){
				var childNode = msgBody.childNodes[0];
				if(childNode.nodeType==Strophe.ElementType.TEXT){
					var jsondata = childNode.wholeText ||childNode.nodeValue;
					jsondata = jsondata.replace('\n','<br>');
					try{
						var data = eval("("+jsondata+")");
						parseMsgData.errorMsg = false;
						parseMsgData.data = [data];
					}catch(e){
					}
				}
			}
		}
		var delayTags = msginfo.getElementsByTagName("delay");
		if(delayTags && delayTags.length>0){
			var delayTag = delayTags[0];
			var delayMsgTime = delayTag.getAttribute("stamp");
			if(delayMsgTime){
				parseMsgData.delayTimeStamp = delayMsgTime;
			}
		}
	} else {
		var childrens = msginfo.childNodes;
		if(childrens&&childrens.length>0){
			var child = msginfo.childNodes[0];
			if(child.nodeType==Strophe.ElementType.TEXT){
				try{
					var data = eval("("+child.nodeValue+")");
					parseMsgData.errorMsg = false;
					parseMsgData.data = [data];
				} catch(e){
				}

			}
		}
	}
	return parseMsgData;
};
var parseFriendFn = function(queryTag){
	var rouster = [];
	var items = queryTag.getElementsByTagName("item");
	if(items){
		for(var i=0;i<items.length;i++){
			var item = items[i];
			var jid = item.getAttribute('jid');
			if(!jid){
				continue;
			}
			var subscription = item.getAttribute('subscription');
			var friend = {
						subscription : subscription,
						jid : jid
			};
			var ask = item.getAttribute('ask');
			if(ask){
				friend.ask = ask;
			}
			var name = item.getAttribute('name');
			if(name){
				friend.name = name;
			} else {
				var n = parseNameFromJidFn(jid);
				friend.name = n;
			}
			var groups = [];
			Strophe.forEachChild(item, 'group',function(group){
				groups.push(Strophe.getText(group));
			});
			friend.groups = groups;
			rouster.push(friend);
		}
	}
	return rouster;
};
var parseRoomFn = function(result){
	var rooms = [];
	var items = result.getElementsByTagName("item");
	if(items){
		for(var i=0;i<items.length;i++){
			var item = items[i];
			var roomJid = item.getAttribute('jid');
			var tmp = roomJid.split("@")[0];
			var room = {
					jid : roomJid,
					name : item.getAttribute('name'),
					roomId : tmp.split('_')[1] 
				};
			rooms.push(room);
		}
	}
	return rooms;
};
var parseRoomOccupantsFn = function(result){
	var occupants = [];
	var items = result.getElementsByTagName("item");
	if(items){
		for(var i=0;i<items.length;i++){
			var item = items[i];
			var room = {
					jid : item.getAttribute('jid'),
					name : item.getAttribute('name')
				};
			occupants.push(room);
		}
	}
	return occupants;
}
var login2UserGrid = function(options){
	options = options || {};

	var appKey = options.appKey || '';
	var devInfos = appKey.split('#');
	if(devInfos.length!=2){
		error({
			type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
			msg : '请指定正确的开发者信息(appKey)'
		});
		return false;
	}
	var orgName = devInfos[0];
	var appName = devInfos[1];
	if(!orgName){
		error({
			type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
			msg : '请指定正确的开发者信息(appKey)'
		});
		return false;
	}
	if(!appName){
		error({
			type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
			msg : '请指定正确的开发者信息(appKey)'
		});
		return false;
	}
	var suc = options.success || emptyFn;
	var error = options.error || emptyFn;
	var user = options.user || '';
	var pwd = options.pwd || '';
	
	var https = options.https;
	var url = https ? 'https://a1.easemob.com' : 'http://a1.easemob.com';
	var apiUrl = options.apiUrl || url;
	
	return dologin2UserGrid(apiUrl,user,pwd,orgName,appName,suc,error);
};
var dologin2UserGrid = function(apiUrl,user,pwd,orgName,appName,suc,error) {
	var loginJson = {
		grant_type : 'password',
		username : user,
		password : pwd
	};
	var loginfo = JSON.stringify(loginJson);
	
	var options = {
		url : apiUrl+"/"+orgName+"/"+appName+"/token",
		dataType : 'json',
		data : loginfo,
		success : suc || emptyFn,
		error : error || emptyFn
	};
	var param = doAjaxRequest(options);
	return param;
};
var innerCheck = function(options,conn){
	if (conn.isOpened() || conn.isOpening()) {
		conn.onError({
			type : EASEMOB_IM_CONNCTION_REOPEN_ERROR,
			msg : '重复打开连接,请先关闭连接再打开'
		});
		return false;
	}
	options = options || {};
	
	var user = options.user || '';
	if (options.user == '') {
		conn.onError({
			type : EASEMOB_IM_CONNCTION_USER_NOT_ASSIGN_ERROR,
			msg : '未指定用户'
		});
		return false;
	}

	var appKey = options.appKey || "";
	var devInfos = appKey.split('#');
	if(devInfos.length!=2){
		conn.onError({
			type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
			msg : '请指定正确的开发者信息(appKey)'
		});
		return false;
	}
	var orgName = devInfos[0];
	var appName = devInfos[1];
	if(!orgName){
		conn.onError({
			type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
			msg : '请指定正确的开发者信息(appKey)'
		});
		return false;
	}
	if(!appName){
		conn.onError({
			type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
			msg : '请指定正确的开发者信息(appKey)'
		});
		return false;
	}
	var jid = appKey + "_" + user + "@" + conn.domain;// jid =
														// {appkey}_{username}@domain/resource
	
	var resource = options.resource || "webim";
	if(resource != ""){
		jid = jid + "/" + resource;
	}
	conn.context.jid = jid;
	conn.context.userId = user;
	conn.context.appKey = appKey;
	conn.context.appName = appName;
	conn.context.orgName = orgName;
	return true;
};
var parseMessageType = function(msginfo){
	var msgtype = 'normal';
	var receiveinfo = msginfo.getElementsByTagName("received");
	if(receiveinfo && receiveinfo.length > 0 && receiveinfo[0].namespaceURI == "urn:xmpp:receipts"){
		msgtype = 'received';
    }else{
    	var inviteinfo =  msginfo.getElementsByTagName("invite");
    	if(inviteinfo && inviteinfo.length > 0){
    		msgtype = 'invite';
    	}
    }
	return msgtype;
};
var login2ImCallback = function (status,msg,conn){
	if (status == Strophe.Status.CONNFAIL){
	  conn.onError({
			type : EASEMOB_IM_CONNCTION_SERVER_CLOSE_ERROR,
			msg : msg
		});
	} else if ((status == Strophe.Status.ATTACHED) || (status == Strophe.Status.CONNECTED)){
		var handleMessage = function(msginfo){
			var type = parseMessageType(msginfo);
			if('received' == type){
				conn.handleReceivedMessage(msginfo);
                return true;
            }else if('invite' == type){
            	conn.handleInviteMessage(msginfo);
            	return true;
            }else{
            	conn.handleMessage(msginfo);
            	return true;
            }
		};
		var handlePresence = function(msginfo){
			conn.handlePresence(msginfo);
			return true;
		};
		var handlePing = function(msginfo){
			conn.handlePing(msginfo);
			return true;
		};
		var handleIq = function(msginfo){
			conn.handleIq(msginfo);
			return true;
		};
		
		conn.addHandler(handleMessage, null, 'message', null, null,  null);
		conn.addHandler(handlePresence, null, 'presence', null, null,  null);
		conn.addHandler(handlePing, "urn:xmpp:ping", 'iq', "get", null,  null);
		conn.addHandler(handleIq, "jabber:iq:roster", 'iq', "set", null,  null);
		
		conn.context.status = STATUS_OPENED;
		var supportRecMessage = [
           EASEMOB_IM_MESSAGE_REC_TEXT,
           EASEMOB_IM_MESSAGE_REC_EMOTION ];
		if (isCanDownLoadFile) {
			supportRecMessage.push(EASEMOB_IM_MESSAGE_REC_PHOTO);
			supportRecMessage.push(EASEMOB_IM_MESSAGE_REC_AUDIO_FILE);
		}
		var supportSedMessage = [ EASEMOB_IM_MESSAGE_SED_TEXT ];
		if (isCanUploadFile) {
			supportSedMessage.push(EASEMOB_IM_MESSAGE_REC_PHOTO);
			supportSedMessage.push(EASEMOB_IM_MESSAGE_REC_AUDIO_FILE);
		}
		conn.onOpened({
			canReceive : supportRecMessage,
			canSend : supportSedMessage,
			accessToken : conn.context.accessToken
		});
	} else if (status == Strophe.Status.DISCONNECTING) {
		if(conn.isOpened()){// 不是主动关闭
			conn.context.status = STATUS_CLOSING;
			conn.onError({
				type : EASEMOB_IM_CONNCTION_SERVER_CLOSE_ERROR,
				msg : msg
			});
		}
	} else if (status == Strophe.Status.DISCONNECTED) {
		conn.context.status = STATUS_CLOSED;
		conn.clear();
		conn.onClosed();
	} else if (status == Strophe.Status.AUTHFAIL){
		conn.onError({
			type : EASEMOB_IM_CONNCTION_AUTH_ERROR,
			msg : '登录失败,请输入正确的用户名和密码'
		});
		conn.clear();
	} else if(status == Strophe.Status.ERROR){
		conn.onError({
			type : EASEMOB_IM_CONNCTION_SERVER_ERROR,
			msg : msg || '服务器异常'
		});
	}
};
var getJid = function(options,conn){
	var jid = options.toJid || '';
	if(jid==''){
		var appKey = conn.context.appKey || '';
		var toJid = appKey + "_" + options.to + "@"
				+ conn.domain;
		if(options.resource){
			toJid = toJid + "/" + options.resource;
		}
		jid = toJid;
	}
	return jid;
};

tempIndex = 0;
var STATUS_INIT = tempIndex++;
var STATUS_DOLOGIN_USERGRID = tempIndex++;
var STATUS_DOLOGIN_IM = tempIndex++;
var STATUS_OPENED = tempIndex++;
var STATUS_CLOSING = tempIndex++;
var STATUS_CLOSED = tempIndex++;

var connection = function() {
}
connection.prototype.init = function(options) {
	var prefix = options.https ? 'https' : 'http';
	this.url = options.url || prefix + '://im-api.easemob.com/http-bind/';
	this.https = options.https || false;
	this.wait = options.wait || 60;
	this.hold = options.hold || 1;
	if(options.route){
		this.route = options.route;
	}

	this.domain = options.domain || "easemob.com";
	this.inactivity = options.inactivity || 60;
	this.maxRetries = options.maxRetries || 5;
	this.pollingTime = options.pollingTime || 800;
	this.stropheConn = false;
	
	this.onOpened = options.onOpened || emptyFn;
	this.onClosed = options.onClosed || emptyFn;
	this.onTextMessage = options.onTextMessage || emptyFn;
	this.onEmotionMessage = options.onEmotionMessage || emptyFn;
	this.onPictureMessage = options.onPictureMessage || emptyFn;
	this.onAudioMessage = options.onAudioMessage || emptyFn;
	this.onVideoMessage = options.onVideoMessage || emptyFn;
	this.onFileMessage = options.onFileMessage || emptyFn;
	this.onLocationMessage = options.onLocationMessage || emptyFn;
	this.onCmdMessage = options.onCmdMessage || emptyFn;
	this.onPresence = options.onPresence || emptyFn;
	this.onRoster = options.onRoster || emptyFn;
	this.onError = options.onError || emptyFn;
	this.onReceivedMessage = options.onReceivedMessage || emptyFn;
	this.onInviteMessage = options.onInviteMessage || emptyFn;
	
	this.context = {
		status : STATUS_INIT
	};
}
var dologin2IM = function(options,conn){ 
	var accessToken = options.access_token || '';
	if(accessToken == ''){
		var loginfo = JSON.stringify(options);
		conn.onError({
			type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
			msg : "登录失败,"+ loginfo,
			data : options,
			xhr : xhr
		});
		return;
	}
	conn.context.accessToken = options.access_token;
	conn.context.accessTokenExpires = options.expires_in;
	var stropheConn = new Strophe.Connection(conn.url,{
						inactivity : conn.inactivity,
						maxRetries : conn.maxRetries,
						pollingTime : conn.pollingTime
	});
	var callback = function(status,msg){
		login2ImCallback(status,msg,conn);
	};
	var jid = conn.context.jid;
	conn.context.stropheConn = stropheConn;
	if(conn.route){
		stropheConn.connect(jid,"$t$" + accessToken,callback,conn.wait,conn.hold,conn.route);
	} else {
		stropheConn.connect(jid,"$t$" + accessToken,callback,conn.wait,conn.hold);
	}

};
// user, pwd, appKey, resource
connection.prototype.open = function(options) {
	var pass = innerCheck(options,this);
	if(pass == false){
		return;
	}
	var conn = this;
	if(options.accessToken){
		options.access_token = options.accessToken;
		dologin2IM(options,conn);
	}else{
		var loginUrl = this.https ? "https://a1.easemob.com" : "http://a1.easemob.com";
		var apiUrl = options.apiUrl || loginUrl;
		var userId = this.context.userId;
		var pwd = options.pwd || '';
		var appName = this.context.appName;
		var orgName = this.context.orgName;
		
		var suc = function(data,xhr){
			conn.context.status = STATUS_DOLOGIN_IM;
			dologin2IM(data,conn);
		};
		var error = function(res,xhr,msg){
			if(res.error && res.error_description){
				conn.onError({
					type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
					msg : "登录失败,"+res.error_description,
					data : res,
					xhr : xhr
				});
			} else {
				conn.onError({
					type : EASEMOB_IM_CONNCTION_OPEN_USERGRID_ERROR,
					msg : "登录失败",
					data : res,
					xhr : xhr
				});
			}
			conn.clear();
		};
		this.context.status = STATUS_DOLOGIN_USERGRID;
		dologin2UserGrid(apiUrl,userId,pwd,orgName,appName,suc,error);
	}
};
connection.prototype.attach = function(options) {
	var pass = innerCheck(options,this);
	if(pass == false)
		return;{
	}
	options = options || {};
	
	var accessToken = options.accessToken || '';
	if(accessToken == ''){
		this.onError({
			type : EASEMOB_IM_CONNCTION_ATTACH_USERGRID_ERROR,
			msg : '未指定用户的accessToken'
		});
		return;
	}

	var sid = options.sid || '';
	if(sid == ''){
		this.onError({
			type : EASEMOB_IM_CONNCTION_ATTACH_ERROR,
			msg : '未指定用户的会话信息'
		});
		return;
	}
	
	var rid = options.rid || '';
	if(rid == ''){
		this.onError({
			type : EASEMOB_IM_CONNCTION_ATTACH_ERROR,
			msg : '未指定用户的消息id'
		});
		return;
	}
	
	var stropheConn = new Strophe.Connection(this.url,{
						inactivity : this.inactivity,
						maxRetries : this.maxRetries,
						pollingTime : this.pollingTime
	});
	
	this.context.accessToken = accessToken;
	this.context.stropheConn = stropheConn;
	this.context.status = STATUS_DOLOGIN_IM;
	
	var conn = this;
	var callback = function(status,msg){
		login2ImCallback(status,msg,conn);
	};
	var jid = this.context.jid;
	var wait = this.wait;
	var hold = this.hold;
	var wind = this.wind || 5;
	stropheConn.attach(jid, sid, rid, callback, wait, hold, wind);
};
connection.prototype.close = function() {
	var status = this.context.status;
	if (status==STATUS_INIT) {
		return;
	}
	if(this.isClosed() || this.isClosing()){
		return;
	}
	this.context.status = STATUS_CLOSING;
	this.context.stropheConn.disconnect();
};
// see stropheConn.addHandler
connection.prototype.addHandler = function (handler, ns, name, type, id, from, options){
	this.context.stropheConn.addHandler(handler, ns, name, type, id, from, options);
};
connection.prototype.handlePresence = function(msginfo){
	if(this.isClosed()){
		return;
	}
	var from = msginfo.getAttribute('from') || '';
	var to = msginfo.getAttribute('to') || '';
	var type = msginfo.getAttribute('type') || '';
	var fromUser = parseNameFromJidFn(from);
	var toUser = parseNameFromJidFn(to);
	var info = {
		from: fromUser,
		to : toUser,
		fromJid : from,
		toJid : to,
		type : type
	};
	
	var showTags = msginfo.getElementsByTagName("show");
	if(showTags && showTags.length>0){
		var showTag = showTags[0];
		info.show = Strophe.getText(showTag);
	}
	var statusTags = msginfo.getElementsByTagName("status");
	if(statusTags && statusTags.length>0){
		var statusTag = statusTags[0];
		info.status = Strophe.getText(statusTag);
	}
	
	var priorityTags = msginfo.getElementsByTagName("priority");
	if(priorityTags && priorityTags.length>0){
		var priorityTag = priorityTags[0];
		info.priority  = Strophe.getText(priorityTag);
	}
	this.onPresence(info,msginfo);
};
connection.prototype.handlePing = function(e) {
	if(this.isClosed()){
		return;
	}
	var id = e.getAttribute('id');
	var from = e.getAttribute('from');
	var to = e.getAttribute('to');
	var dom = $iq({
				from : to,
				to : from,
				id : id,
				type : 'result'
	});
	this.sendCommand(dom.tree());
};
connection.prototype.handleIq = function(e) {
	var id = e.getAttribute('id');
	var from = e.getAttribute('from') || '';
	var name = parseNameFromJidFn(from);
	var curJid = this.context.jid;
	var curUser = this.context.userId;
	if (from !== "" && from != curJid && curUser != name)
		return true;
		
	var iqresult = $iq({type: 'result', id: id, from: curJid});
	this.sendCommand(iqresult.tree());
	
	var msgBodies = e.getElementsByTagName("query");
	if(msgBodies&&msgBodies.length>0){
		var queryTag = msgBodies[0];
		var rouster = parseFriendFn(queryTag);
		this.onRoster(rouster);
	}
	return true;
};
connection.prototype.handleMessage = function(msginfo){
	if(this.isClosed()){
		return;
	}
	var id = msginfo.getAttribute('id') || '';
	this.sendReceiptsMessage({
		id : id
	});
	var parseMsgData = parseResponseMessageFn(msginfo);
	if(parseMsgData.errorMsg){
		return;
	}
	var msgDatas = parseMsgData.data;
	for(var i in msgDatas){
		var msg = msgDatas[i];
		var from = msg.from;
		var too = msg.to;
		var extmsg = msg.ext || {};
		var chattype = msginfo.getAttribute('type') || 'chat';
		var msgBodies = msg.bodies;
		if(!msgBodies || msgBodies.length==0){
			continue;
		}
		var msgBody = msg.bodies[0];
		var type = msgBody.type;
		if ("txt" == type) {
			var receiveMsg = msgBody.msg;
			var emotionsbody = parseTextMessageFn(receiveMsg);
			if(emotionsbody.isemotion){
				this.onEmotionMessage({
					type : chattype,
					from : from,
					to : too,
					data : emotionsbody.body,
					ext : extmsg
				});
			} else {
				this.onTextMessage({
					type : chattype,
					from : from,
					to : too,
					data : receiveMsg,
					ext : extmsg
				});
			}
		} else if ("img" == type) {
			var rwidth = 0;
			var rheight = 0;
			if(msgBody.size){
				rwidth = msgBody.size.width;
				rheight = msgBody.size.height;
			}
			var msg = {
				type : chattype,
				from : from,
				to : too,
				url : msgBody.url,
				secret : msgBody.secret,
				filename : msgBody.filename,
				thumb : msgBody.thumb,
				thumb_secret : msgBody.thumb_secret,
				file_length : msgBody.file_length||'',
				width : rwidth,
				height : rheight,
				filetype : msgBody.filetype||'',
				accessToken : this.context.accessToken || '',
				ext : extmsg
			};
			this.onPictureMessage(msg);
		} else if ("audio" == type) {
			this.onAudioMessage({
				type : chattype,
				from : from,
				to : too,
				url : msgBody.url,
				secret : msgBody.secret,
				filename : msgBody.filename,
				length : msgBody.length||'',
				file_length : msgBody.file_length||'',
				filetype : msgBody.filetype||'',
				accessToken : this.context.accessToken || '',
				ext : extmsg
			});
		} else if ("file" == type) {
			this.onFileMessage({
				type : chattype,
				from : from,
				to : too,
				url : msgBody.url,
				secret : msgBody.secret,
				filename : msgBody.filename,
				file_length : msgBody.file_length,
				accessToken : this.context.accessToken || '',
				ext : extmsg
			});
		} else if ("loc" == type) {
			this.onLocationMessage({
				type : chattype,
				from : from,
				to : too,
				addr : msgBody.addr,
				lat : msgBody.lat,
				lng : msgBody.lng,
				ext : extmsg
			});
		}else if("video" == type){
			this.onVideoMessage({
				type : chattype,
				from : from,
				to : too,
				url : msgBody.url,
				secret : msgBody.secret,
				filename : msgBody.filename,
				file_length : msgBody.file_length,
				accessToken : this.context.accessToken || '',
				ext : extmsg
			});
		}else if("cmd" == type){
			this.onCmdMessage({
				from : from,
				to : too,
				action : msgBody.action,
				ext : extmsg
			});
		}
	}
};
connection.prototype.handleReceivedMessage = function(message){
	this.onReceivedMessage(message);
};
connection.prototype.handleInviteMessage = function(message){
	var form = null;
	var invitemsg = message.getElementsByTagName('invite');
	if(invitemsg && invitemsg.length>0){
		var fromJid = invitemsg[0].getAttribute('from');
		form = parseNameFromJidFn(fromJid);
	}
	var xmsg = message.getElementsByTagName('x');
	var roomid = null;
	if(xmsg && xmsg.length > 0){
		for(var i = 0; i < xmsg.length; i++){
			if('jabber:x:conference' == xmsg[i].namespaceURI){
				var roomjid = xmsg[i].getAttribute('jid');
				roomid = parseNameFromJidFn(roomjid);
			}
		}
	}
	this.onInviteMessage({
		type : 'invite',
		from : form,
		roomid : roomid
	});
};
connection.prototype.sendCommand = function(dom) {
	if(this.isOpened()){
		this.context.stropheConn.send(dom);
	} else {
		this.onError({
			type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
			msg : '连接还未建立,请先登录或等待登录处理完毕'
		});
	}
};
connection.prototype.getUniqueId = function (prefix)
{
	var cdate = new Date();
	var offdate = new Date(2010,1,1);
		var offset = cdate.getTime()-offdate.getTime();
		var hexd = parseInt(offset).toString(16);
    if (typeof(prefix) == "string" || typeof(prefix) == "number") {
        return prefix+"_"+hexd;
    } else {
        return 'WEBIM_'+hexd;
    }
};
connection.prototype.sendTextMessage = function(options) {
	var appKey = this.context.appKey || '';
	var toJid = appKey + "_" + options.to + "@"	+ this.domain;
	if(options.type && options.type == 'groupchat'){
		toJid = appKey + "_"+options.to+'@conference.' + this.domain;
	}
	if(options.resource){
		toJid = toJid + "/" + options.resource;
	}
	var msgTxt = options.msg;
	var json = {
		from : this.context.userId || '',
		to : 	options.to,
		bodies : [{
			type : "txt",
			msg : msgTxt
		}],
		ext : options.ext || {}
	};
	var jsonstr = JSON.stringify(json);
	var dom = $msg({
		to : toJid, 
		type : options.type || 'chat', 
		id : this.getUniqueId(),
		xmlns : "jabber:client"
	}).c("body").t(jsonstr);
	this.sendCommand(dom.tree());
};
connection.prototype.sendPicture = function(options) {
	var onerror =  options.onFileUploadError || this.onError || emptyFn;
	if(!isCanUploadFile){
	  onerror({
			type : EASEMOB_IM_UPLOADFILE_BROWSER_ERROR,
			msg : '当前浏览器不支持异步上传文件,请换用其他浏览器'
		});
	  return;
	}
	var conn = this;
	var onFileUploadComplete = options.onFileUploadComplete || emptyFn;
	var myUploadComplete = function(data) {
		options["url"] = data.uri;
		options["secret"] = data.entities[0]["share-secret"];
		if(data.entities[0]["file-metadata"]){
			var file_len = data.entities[0]["file-metadata"]["content-length"];
			options["file_length"] = file_len;
			options["filetype"] = data.entities[0]["file-metadata"]["content-type"]
			if (file_len > 204800) {
				options["thumbnail"] = true;
			}
		}
		options["uuid"] = data.entities[0].uuid;
		
		onFileUploadComplete(data);
		conn.sendPictureMessage(options);
	};
	options.onFileUploadComplete = myUploadComplete;
	options.onFileUploadError = options.onFileUploadError|| this.onError || emptyFn;

	var image = new Image();
	var imageLoadFn = function() {
		image.onload = null;
		if (!this.readyState || this.readyState == 'loaded'
				|| this.readyState == 'complete') {
			var heigth = image.height;
			var width = image.width;
			options.height = heigth;
			options.width = width;
			options.appName = conn.context.appName || '';
			options.orgName = conn.context.orgName || '';
			options.accessToken = conn.context.accessToken || '';
			uploadFn(options);
		};
	};
	if("onload" in image){
		image.onload = imageLoadFn;
	} else {
		image.onreadystatechange = imageLoadFn;
	}
	image.onerror = function() {
		image.onerror = function(){
			image.onerror = null;
			options.onFileUploadError({
				type : EASEMOB_IM_UPLOADFILE_ERROR,
				msg : '指定的图片不存在或者不是一个图片格式文件'
			});
		};
		image.src = document.getElementById(options.fileInputId).value;
	};
	var picId = options.fileInputId;
	file = getFileUrlFn(picId);
	options.fileInfo = file;
	options.filename = file.filename;
	
	if (!file.url) {
		options.onFileUploadError({
			type : EASEMOB_IM_UPLOADFILE_NO_FILE,
			msg : '未选择上传文件'
		});
	} else {
		image.src = file.url;
	}
};
connection.prototype.sendPictureMessage = function(options) {
	var appKey = this.context.appKey || '';
	var toJid = appKey + "_" + options.to + "@"	+ this.domain;
	if(options.type && options.type == 'groupchat'){
		toJid = appKey + "_"+options.to+'@conference.' + this.domain;
	}
	if(options.resource){
		toJid = toJid + "/" + options.resource;
	}
	
	var json = {
				from : this.context.userId || '',
				to : 	options.to,
				bodies :[{
						type : "img",
						url : options.url + '/' + options.uuid,
						secret : options.secret,
						filename : options.filename,
						thumb : options.url + '/' + options.uuid,
						thumb_secret : '',
						size : {
							width : options.width,
							height : options.height
						},
						"file_length" : options.file_length,
						filetype : options.filetype
				}],
				ext : options.ext || {}
	};
	var jsonstr = JSON.stringify(json);
	var date = new Date();
	var dom = $msg({
				type : options.type || 'chat',
				to : toJid,
				id : this.getUniqueId(),
				xmlns : "jabber:client"
	}).c("body").t(jsonstr);
	this.sendCommand(dom.tree());
};
connection.prototype.sendAudio = function(options) {
	var onerror =  options.onFileUploadError || this.onError || emptyFn;
	if(!isCanUploadFile){
	  onerror({
			type : EASEMOB_IM_UPLOADFILE_BROWSER_ERROR,
			msg : '当前浏览器不支持异步上传文件,请换用其他浏览器'
		});
	  return;
	}
	var conn = this;
	var onFileUploadComplete = options.onFileUploadComplete || emptyFn;
	var myonComplete = function(data) {
		onFileUploadComplete(data);
		options["url"] = data.uri;
		options["secret"] = data.entities[0]["share-secret"];
		if(data.entities[0]["file-metadata"]){
			options["file_length"] = data.entities[0]["file-metadata"]["content-length"];
			options["filetype"] = data.entities[0]["file-metadata"]["content-type"];
		}
		options["uuid"] = data.entities[0].uuid;
		options["length"] = data.duration;
		conn.sendAudioMessage(options);
	};
	options.appName = this.context.appName || '';
	options.orgName = this.context.orgName || '';
	options.accessToken = this.context.accessToken || '';
	options.onFileUploadComplete = myonComplete;
			
	var file = getFileUrlFn(options.fileInputId);
	options.fileInfo = file;
	options.filename = file.filename;
	
	uploadFn(options, this);
};
connection.prototype.sendAudioMessage = function(options) {
	var appKey = this.context.appKey || '';
	var toJid = appKey + "_" + options.to + "@"	+ this.domain;
	if(options.type && options.type == 'groupchat'){
		toJid =appKey + "_"+options.to+'@conference.' + this.domain;
	}
	if(options.resource){
		toJid = toJid + "/" + options.resource;
	}
	
	var json = {
				from : this.context.userId || '',
				to : 	options.to,
				bodies :[{
						type : "audio",
						url : options.url + '/' + options.uuid,
						secret : options.secret,
						filename : options.filename,
						"file_length" : options.file_length,
						length : options.length
				}],
				ext : options.ext || {}
	};
	var jsonstr = JSON.stringify(json);
	var dom = $msg({
				type : options.type || 'chat',
				to : toJid,
				id : this.getUniqueId(),
				xmlns : "jabber:client"
	}).c("body").t(jsonstr);
	this.sendCommand(dom.tree());
};
connection.prototype.sendFileMessage = function(options) {
	var appKey = this.context.appKey || '';
	var toJid = appKey + "_" + options.to + "@"	+ this.domain;
	if(options.type && options.type == 'groupchat'){
		toJid =appKey + "_"+options.to+'@conference.' + this.domain;
	}
	
	if(options.resource){
		toJid = toJid + "/" + options.resource;
	}
	var json = {
				from : this.context.userId || '',
				to : 	options.to,
				bodies :[{
						type : "file",
						url : options.url,
						secret : options.secret,
						filename : options.filename,
						"file_length" : options.file_length
				}],
				ext : options.ext || {}
	};
	var jsonstr = JSON.stringify(json);
	var dom = $msg({
				type : 'chat',
				to : toJid,
				id : this.getUniqueId(),
				xmlns : "jabber:client"
	}).c("body").t(jsonstr);
	this.sendCommand(dom.tree());
};
connection.prototype.sendLocationMessage = function(options) {
	var appKey = this.context.appKey || '';
	var toJid = appKey + "_" + options.to + "@"	+ this.domain;
	if(options.type && options.type == 'groupchat'){
		toJid =appKey + "_"+options.to+'@conference.' + this.domain;
	}
	
	if(options.resource){
		toJid = toJid + "/" + options.resource;
	}
	var json = {
				from : this.context.userId || '',
				to : 	options.to,
				bodies :[{
						type : "loc",
						addr : options.addr,
						lat : options.lat,
						lng : options.lng
				}],
				ext : options.ext || {}
	};
	var jsonstr = JSON.stringify(json);
	var dom = $msg({
				type : 'chat',
				to : toJid,
				id : this.getUniqueId(),
				xmlns : "jabber:client"
	}).c("body").t(jsonstr);
	this.sendCommand(dom.tree());
};
connection.prototype.sendReceiptsMessage = function(options){
	var dom = $msg({
				from : this.context.jid || '',
				to : "easemob.com",
				id : options.id || ''
	}).c("received",{
				xmlns : "urn:xmpp:receipts",
				id : options.id || ''
			});
	this.sendCommand(dom.tree());
};
connection.prototype.addRoster = function(options){
	var jid = getJid(options,this);
	var name = options.name || '';
	var groups = options.groups || '';

	var iq = $iq({type : 'set'});
	iq.c("query",{xmlns:'jabber:iq:roster'});
	iq.c("item",{jid: jid ,name : name});
	
	if(groups){
		for (var i = 0; i < groups.length; i++){
			iq.c('group').t(groups[i]).up();
		}
	}
	var suc = options.success || emptyFn;
	var error = options.error || emptyFn;
	this.context.stropheConn.sendIQ(iq.tree(),suc,error);
};
connection.prototype.removeRoster = function(options){
	var jid = getJid(options,this);
	var iq = $iq({type: 'set'}).c('query', {xmlns : "jabber:iq:roster"}).c('item', {jid: jid,subscription: "remove"});
	
	var suc = options.success || emptyFn;
	var error = options.error || emptyFn;
	this.context.stropheConn.sendIQ(iq,suc,error);
};
connection.prototype.getRoster = function(options) {
	var conn = this;
	var dom  = $iq({
      	type: 'get'
  }).c('query', {xmlns: 'jabber:iq:roster'});

	options = options || {};
	suc = options.success || this.onRoster; 
  var completeFn = function(ele){
  	var rouster = [];
		var msgBodies = ele.getElementsByTagName("query");
		if(msgBodies&&msgBodies.length>0){
			var queryTag = msgBodies[0];
			rouster = parseFriendFn(queryTag);
		}
  	suc(rouster,ele);
  };
  error = options.error || this.onError;
  var failFn = function(ele){
		error({
			type : EASEMOB_IM_CONNCTION_GETROSTER_ERROR,
			msg : '获取联系人信息失败',
			data : ele
		});
  };
	if(this.isOpened()){
		this.context.stropheConn.sendIQ(dom.tree(),completeFn,failFn);
	} else {
		error({
			type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
			msg : '连接还未建立,请先登录或等待登录处理完毕'
		});
	}
};
connection.prototype.subscribe = function(options) {
	var jid = getJid(options,this);
	var pres = $pres({to: jid, type: "subscribe"});
	if (options.message) {
		pres.c("status").t(options.message).up();
	}
	if (options.nick) {
		pres.c('nick', {'xmlns': "http://jabber.org/protocol/nick"}).t(options.nick);
	}
	this.sendCommand(pres.tree());
};
connection.prototype.subscribed = function(options) {
	var jid = getJid(options,this);
	var pres = $pres({to : jid, type : "subscribed"});
	if (options.message) {
		pres.c("status").t(options.message).up();
	}
	this.sendCommand(pres.tree());
};
connection.prototype.unsubscribe = function(options) {
	var jid = getJid(options,this);
	var pres = $pres({to : jid, type : "unsubscribe"});
	if (options.message) {
		pres.c("status").t(options.message);
	}
	this.sendCommand(pres.tree());
};
connection.prototype.unsubscribed = function(options) {
	var jid = getJid(options,this);
	var pres = $pres({to : jid, type : "unsubscribed"});
	if (options.message) {
		pres.c("status").t(options.message).up();
	}
	this.sendCommand(pres.tree());
 };
 
connection.prototype.createRoom = function(options) {
	var suc =options.success || emptyFn;
	var err =  options.error || emptyFn;
	var roomiq;
	roomiq = $iq({
		to: options.rooomName,
		type: "set"
	}).c("query", {
		xmlns: Strophe.NS.MUC_OWNER
	}).c("x", {
		 xmlns: "jabber:x:data",
		 type: "submit"
	});
	return this.context.stropheConn.sendIQ(roomiq.tree(), suc, err);
};
 
connection.prototype.join = function(options){
	var roomJid = this.context.appKey+"_"+options.roomId+'@conference.' + this.domain;
	var room_nick = roomJid+"/"+this.context.userId;
	var suc =options.success || emptyFn;
	var err =  options.error || emptyFn;
	var errorFn = function (ele){
		err({
			type : EASEMOB_IM_CONNCTION_JOINROOM_ERROR,
			msg : '加入房间失败',
			data : ele
		});
	}
	var iq = $pres({
		from: this.context.jid,
		to: room_nick
	}).c("x", {
		xmlns: Strophe.NS.MUC
	});
	this.context.stropheConn.sendIQ(iq.tree(), suc, errorFn);
};
connection.prototype.listRooms = function(options) {
    var iq;
    iq = $iq({
      to: options.server||'conference.' + this.domain,
      from: this.context.jid,
      type: "get"
    }).c("query", {
      xmlns: Strophe.NS.DISCO_ITEMS
    });
    var suc =options.success || emptyFn;
	var completeFn = function(result){
		var rooms = [];
		rooms = parseRoomFn(result);
		suc(rooms);
	}
	var err =  options.error || emptyFn;
	var errorFn = function (ele){
		err({
			type : EASEMOB_IM_CONNCTION_GETROOM_ERROR,
			msg : '获取群组列表失败',
			data : ele
		});
	}
    this.context.stropheConn.sendIQ(iq.tree(), completeFn, errorFn);
};

connection.prototype.queryRoomMember = function(options){
	var domain = this.domain;
	var members = [];
	 var iq= $iq({
	      to : this.context.appKey+"_"+options.roomId+'@conference.' + this.domain,
	      type : 'get'
	    }).c('query', {
	    	xmlns: Strophe.NS.MUC+'#admin'
	    }).c('item',{
	    	affiliation:'member'
	    });
    var suc =options.success || emptyFn;
	var completeFn = function(result){
		var items = result.getElementsByTagName('item');
		if(items){
			for(var i=0;i<items.length;i++){
				var item = items[i];
				var mem = {
						jid : item.getAttribute('jid'),
						affiliation : 'member'
					};
				members.push(mem);
			}
		}
		suc(members);
	}
	var err =  options.error || emptyFn;
	var errorFn = function (ele){
		err({
			type : EASEMOB_IM_CONNCTION_GETROOMMEMBER_ERROR,
			msg : '获取群组成员列表失败',
			data : ele
		});
	}
    this.context.stropheConn.sendIQ(iq.tree(), completeFn, errorFn);
};

connection.prototype.queryRoomInfo = function(options){
	var domain = this.domain;
	var iq= $iq({
	      to:  this.context.appKey+"_"+options.roomId+'@conference.' + domain,
	      type: "get"
	    }).c("query", {
	      xmlns: Strophe.NS.DISCO_INFO
	    });
    var suc =options.success || emptyFn;
    var members = [];
	var completeFn = function(result){
		var fields = result.getElementsByTagName('field');
		if(fields){
			for(var i=0;i<fields.length;i++){
				var field = fields[i];
				if(field.getAttribute('label') == 'owner'){
					var mem = {
							jid : field.textContent + "@" + domain,
							affiliation : 'owner'
						};
					members.push(mem);
				}
			}
		}
		suc(members);
	}
	var err =  options.error || emptyFn;
	var errorFn = function (ele){
		err({
			type : EASEMOB_IM_CONNCTION_GETROOMINFO_ERROR,
			msg : '获取群组信息失败',
			data : ele
		});
	}
    this.context.stropheConn.sendIQ(iq.tree(), completeFn, errorFn);
};

connection.prototype.queryRoomOccupants = function(options) {
	var suc =options.success || emptyFn;
	var completeFn = function(result){
		var occupants = [];
		occupants = parseRoomOccupantsFn(result);
		suc(occupants);
	}
	var err =  options.error || emptyFn;
	var errorFn = function (ele){
		err({
			type : EASEMOB_IM_CONNCTION_GETROOMOCCUPANTS_ERROR,
			msg : '获取群组出席者列表失败',
			data : ele
		});
	}
    var attrs = {
      xmlns: Strophe.NS.DISCO_ITEMS
    };
    var info = $iq({
      from : this.context.jid,
      to : this.context.appKey+"_"+options.roomId+'@conference.' + this.domain,
      type : 'get'
    }).c('query', attrs);
    this.context.stropheConn.sendIQ(info.tree(), completeFn, errorFn);
  };

connection.prototype.setUserSig = function(desc) {
	var dom = $pres({xmlns : 'jabber:client'});
	desc = desc || "";
	dom.c("status").t(desc);
	this.sendCommand(dom.tree());
};
connection.prototype.setPresence = function(type,status) {
	var dom = $pres({xmlns : 'jabber:client'});
	if (type){
		if(status){
			dom.c("show").t(type);
			dom.up().c("status").t(status);
		} else {
			dom.c("show").t(type);
		}
	}
	this.sendCommand(dom.tree());
};
connection.prototype.getPresence = function() {
	var dom = $pres({xmlns : 'jabber:client'});
	var conn = this;
	this.sendCommand(dom.tree());
};
connection.prototype.ping = function(options) {
	options = options || {};
	var jid = getJid(options,this);

	var dom = $iq({
		from : this.context.jid || '',
		to: jid,
		type: "get"
	}).c("ping", {xmlns: "urn:xmpp:ping"});

	suc = options.success || emptyFn;
	error = options.error || this.onError;
	var failFn = function(ele){
		error({
			type : EASEMOB_IM_CONNCTION_PING_ERROR,
			msg : 'ping失败',
			data : ele
		});
	};
	if(this.isOpened()){
		this.context.stropheConn.sendIQ(dom.tree(),suc,failFn);
	} else {
		error({
			type : EASEMOB_IM_CONNCTION_OPEN_ERROR,
			msg : '连接还未建立,请先登录或等待登录处理完毕'
		});
	}
	return;
};
connection.prototype.isOpened = function() {
	var status = this.context.status;
	return status==STATUS_OPENED;
};
connection.prototype.isOpening = function() {
	var status = this.context.status;
	return (status==STATUS_DOLOGIN_USERGRID) || (status==STATUS_DOLOGIN_IM);
};
connection.prototype.isClosing = function() {
	var status = this.context.status;
	return (status==STATUS_CLOSING);
};
connection.prototype.isClosed = function() {
	var status = this.context.status;
	return status == STATUS_CLOSED;
};
connection.prototype.clear = function() {
	this.context = {
		status : STATUS_INIT
	};
};


Easemob.im.Connection = connection;

if (typeof Easemob.im.Helper == 'undefined') {
	Easemob.im.Helper = {};
	
	// method
	Easemob.im.Helper.getFileUrl = getFileUrlFn;
	Easemob.im.Helper.upload = uploadFn;
	Easemob.im.Helper.download = downloadFn;
	Easemob.im.Helper.getFileSize = getFileSizeFn;
	Easemob.im.Helper.xhr = doAjaxRequest;
	Easemob.im.Helper.parseTextMessage = parseTextMessageFn;
	Easemob.im.Helper.login2UserGrid = login2UserGrid;

	// attritue
	Easemob.im.Helper.isCanUploadFile = isCanUploadFile;
	Easemob.im.Helper.isCanDownLoadFile = isCanDownLoadFile;
	Easemob.im.Helper.hasSetRequestHeader = hasSetRequestHeader;
	Easemob.im.Helper.hasOverrideMimeType = hasOverrideMimeType;
	
	// object
	Easemob.im.Helper.Base64 = innerBase64;
	Easemob.im.Helper.EmotionPicData = emotionPicData;
	
	//user
	Easemob.im.Helper.registerUser = registerUserFn;
}
})(jQuery)
}
